home *** CD-ROM | disk | FTP | other *** search
/ boe.pres.k12.wv.us / boe.pres.k12.wv.us.zip / boe.pres.k12.wv.us / Utilities / Xerox Workcentre 5335 / Windows Scan / 64-bit_x64 / Japanese / cpsimage.cab / data / sys / XmlWriter.elf < prev    next >
Text File  |  2009-04-23  |  34KB  |  991 lines

  1. #load "sys/collections.elf";
  2.  
  3. /*****************************************************************************/
  4. /*
  5. ** This script contains a set of classes for the dynamic generation of a
  6. ** well-formed XML document. The classes provide the means to incrementally
  7. ** build and write a formatted or unformatted XML document to a file or a
  8. ** string via an XmlWriter instance.
  9. **
  10. ** An XmlWriter instance is created using the XmlWriterFactory class. The
  11. ** XmlWriterFactory.createFileWriter method can be used to create a writer for
  12. ** writing XML data to a file. The XmlWriterFactory.createStringWriter method
  13. ** can be used to create a writer for writing XML data to a string.
  14. */
  15. /*****************************************************************************/
  16.  
  17. CLASS Xml {
  18.    STRING XmlNamespaceUri   = "http://www.w3.org/XML/1998/namespace";
  19.    STRING XmlnsNamespaceUri = "http://www.w3.org/2000/xmlns/";
  20. }
  21.  
  22. CLASS XmlSpace {
  23.    INTEGER None     = 0;
  24.    INTEGER Default  = 1;
  25.    INTEGER Preserve = 2;
  26. }
  27.  
  28. /*****************************************************************************/
  29. /*
  30. ** This class is the abstract base class for state objects representing the
  31. ** current state of an XmlWriter instance.
  32. */
  33. /* @writeDocumentStart Writes the XML declaration. */
  34. /* @writeDocumentEnd Writes end tags for any open XML elements or attributes and puts the writer in the End state. */
  35. /* @writeElementStart Writes specified XML element start tag. */
  36. /* @writeElementEnd Writes end tag for the last XML element started with 'writeElementStart' call. */
  37. /* @writeString Writes the given XML text content. */
  38. /* @writeAttributeStart Writes the start of an XML attribute. */
  39. /* @writeAttributeEnd Writes the end for last XML attribute started with 'writeAttributeStart' call. */
  40. /* @writeComment Writes an XML comment containing specified text. */
  41. /* @writeRaw Writes raw XML data from a string (with no validation). */
  42. /*****************************************************************************/
  43. private CLASS XmlWriterState {
  44.    // Public Methods
  45.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  46.    }
  47.  
  48.    METHOD writeDocumentEnd (XmlWriter writer) {
  49.    }
  50.  
  51.    METHOD writeElementStart (XmlWriter writer, STRING prefix,
  52.                              STRING localName, STRING ns) {
  53.    }
  54.  
  55.    METHOD writeElementEnd (XmlWriter writer) {
  56.    }
  57.  
  58.    METHOD writeString (XmlWriter writer, STRING text) {
  59.    }
  60.  
  61.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  62.                                STRING localName, STRING ns) {
  63.    }
  64.  
  65.    METHOD writeAttributeEnd (XmlWriter writer) {
  66.    }
  67.  
  68.    METHOD writeComment (XmlWriter writer, STRING text) {
  69.    }
  70.  
  71.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  72.    }
  73. }
  74.  
  75. /*****************************************************************************/
  76. /*
  77. ** This class is the XmlWriterState representing the initial state of an
  78. ** XmlWriter instance in which no XML data has been written.
  79. */
  80. /****************************************************************************/
  81. private CLASS XmlStartWriterState EXTENDS XmlWriterState {
  82.    // Public Methods
  83.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  84.       if (!encoding)
  85.          encoding = "utf-8";
  86.  
  87.       STRING prolog = "<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>";
  88.       writer.write (str: prolog);
  89.       writer.setState (state: XmlWriter.prologWriterState);
  90.    }
  91.  
  92.    METHOD writeDocumentEnd (XmlWriter writer) {
  93.       print "InvalidOperationException: No root element present";
  94.    }
  95.  
  96.    METHOD writeElementStart (XmlWriter writer, STRING prefix, 
  97.                              STRING localName, STRING ns) {
  98.       writer.writeElementStartInternal (prefix: prefix,
  99.          localName: localName, ns: ns);
  100.       writer.setState (state: XmlWriter.elementWriterState);
  101.    }
  102.  
  103.    METHOD writeElementEnd (XmlWriter writer) {
  104.       print "InvalidOperationException: No root element present";
  105.    }
  106.  
  107.    METHOD writeString (XmlWriter writer, STRING text) {
  108.       print "InvalidOperationException: No root element present";
  109.    }
  110.  
  111.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  112.                                STRING localName, STRING ns) {
  113.       print "InvalidOperationException: Cannot write attribute start outside element start";
  114.    }
  115.  
  116.    METHOD writeAttributeEnd (XmlWriter writer) {
  117.       print "InvalidOperationException: Cannot write attribute end outside element start";
  118.    }
  119.  
  120.    METHOD writeComment (XmlWriter writer, STRING text) {
  121.       writer.write (str: "<!-- ");
  122.       writer.write (str: text);
  123.       writer.write (str: " -->");
  124.    }
  125.  
  126.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  127.       writer.write (str: xmlstr);
  128.       writer.setState (state: XmlWriter.endWriterState);
  129.    }
  130. }
  131.  
  132. /*****************************************************************************/
  133. /*
  134. ** This class is the XmlWriterState representing the state of an XmlWriter
  135. ** instance in which the XML prolog has been written.
  136. */
  137. /****************************************************************************/
  138. private CLASS XmlPrologWriterState EXTENDS XmlWriterState {
  139.    // Public Methods
  140.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  141.        print "InvalidOperationException: Cannot have more than one XML declaration";
  142.    }
  143.  
  144.    METHOD writeDocumentEnd (XmlWriter writer) {
  145.       print "InvalidOperationException: No root element present";
  146.    }
  147.  
  148.    METHOD writeElementStart (XmlWriter writer, STRING prefix, 
  149.                              STRING localName, STRING ns) {
  150.       writer.newlineAndIndent ();
  151.       writer.writeElementStartInternal (prefix: prefix,
  152.          localName: localName, ns: ns);
  153.       writer.setState (state: XmlWriter.elementWriterState);
  154.    }
  155.  
  156.    METHOD writeElementEnd (XmlWriter writer) {
  157.       print "InvalidOperationException: No root element present";
  158.    }
  159.  
  160.    METHOD writeString (XmlWriter writer, STRING text) {
  161.       print "InvalidOperationException: No root element present";
  162.    }
  163.  
  164.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  165.                                STRING localName, STRING ns) {
  166.       print "Cannot write attribute start outside element start";
  167.    }
  168.  
  169.    METHOD writeAttributeEnd (XmlWriter writer) {
  170.       print "Cannot write attribute end outside element start";
  171.    }
  172.  
  173.    METHOD writeComment (XmlWriter writer, STRING text) {
  174.       writer.newlineAndIndent ();
  175.       writer.write (str: "<!-- ");
  176.       writer.write (str: text);
  177.       writer.write (str: " -->");
  178.    }
  179.  
  180.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  181.       writer.write (str: "\n");
  182.       writer.write (str: xmlstr);
  183.       writer.setState (state: XmlWriter.endWriterState);
  184.    }
  185. }
  186.  
  187. /*****************************************************************************/
  188. /*
  189. ** This class is the XmlWriterState representing the state of an XmlWriter
  190. ** instance in which an XML element start tag is being written.
  191. */
  192. /****************************************************************************/
  193. private CLASS XmlElementWriterState EXTENDS XmlWriterState {
  194.    // Public Methods
  195.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  196.       print "InvalidOperationException: Cannot write XML declaration inside element";
  197.    }
  198.  
  199.    METHOD writeDocumentEnd (XmlWriter writer) {
  200.       BOOLEAN done;
  201.       for (done = FALSE; done; ) {
  202.          if (writer.openElems.isEmpty ())
  203.             done = TRUE;
  204.          else
  205.             this.writeElementEnd (writer: writer);
  206.          }
  207.       writer.setState (state: XmlWriter.endWriterState);
  208.    }
  209.  
  210.    METHOD writeElementStart (XmlWriter writer, STRING prefix, 
  211.                              STRING localName, STRING ns) {
  212.       writer.writeNamespaces ();
  213.       writer.write (str: ">");
  214.       writer.newlineAndIndent ();
  215.       writer.writeElementStartInternal (prefix: prefix,
  216.          localName: localName, ns: ns);
  217.    }
  218.  
  219.    METHOD writeElementEnd (XmlWriter writer) {
  220.       writer.writeNamespaces ();
  221.       writer.write (str: "/>");
  222.       writer.openElems.pop ();
  223.       writer.setState (state: XmlWriter.contentWriterState);
  224.    }
  225.  
  226.    METHOD writeString (XmlWriter writer, STRING text) {
  227.       writer.writeNamespaces ();
  228.       writer.write (str: ">");
  229.       writer.setState (state: XmlWriter.contentWriterState);
  230.       writer.currentState.writeString (writer: writer, text: text);
  231.    }
  232.  
  233.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  234.                                STRING localName, STRING ns) {
  235.       XmlOpenElement elem = writer.openElems.peek ();
  236.  
  237.       if (prefix == "xml") {
  238.          ns = Xml.XmlNamespaceUri;
  239.          if (localName == "lang")
  240.             elem.openXmlLang = TRUE;
  241.          else if (localName == "space")
  242.             elem.openXmlSpace = TRUE;
  243.          }
  244.  
  245.       if ((prefix.length () > 0) && (ns.length () == 0)) {
  246.          if (prefix != "xmlns") {
  247.             print "ArgumentException: Cannot use prefix <" + prefix +
  248.                      "> with empty namespace";
  249.             return;
  250.             }
  251.          }
  252.  
  253.       if (prefix == "xmlns") {
  254.          if (localName.length () == 0) {
  255.             localName = prefix;
  256.             prefix    = "";
  257.             }
  258.          }
  259.  
  260.       if ((prefix == "xmlns" || localName == "xmlns") &&
  261.           (ns && (ns != Xml.XmlnsNamespaceUri))) {
  262.          print "ArgumentException: xmlns prefix is bound to namespace" +
  263.             Xml.XmlnsNamespaceUri;
  264.          return;
  265.          }
  266.  
  267.       if (ns == Xml.XmlnsNamespaceUri) {
  268.          prefix = "";
  269.          if (localName != "xmlns")
  270.             prefix = "xmlns";
  271.          }
  272.  
  273.       if (ns && prefix != "xmlns") {
  274.          STRING  existingPrefix = elem.lookupPrefix (ns: ns);
  275.          if (existingPrefix == "") {
  276.             if ((prefix == "") || elem.lookupNamespace (prefix: prefix))
  277.                prefix = elem.generatePrefix ();
  278.             }
  279.  
  280.          if ((prefix == "") && (ns != Xml.XmlnsNamespaceUri))
  281.             prefix = existingPrefix;
  282.          }
  283.  
  284.       STRING qName = localName;
  285.       if (prefix.length () != 0)
  286.          qName = prefix + ":" + qName;
  287.  
  288.       writer.writeAttributeIndent ();
  289.       writer.write (str: qName);
  290.       writer.write (str:  "=\"");
  291.       writer.setState (state: XmlWriter.attributeWriterState);
  292.    }
  293.  
  294.    METHOD writeAttributeEnd (XmlWriter writer) {
  295.       print "InvalidOperationException: Cannot write attribute end outside element start";
  296.    }
  297.  
  298.    METHOD writeComment (XmlWriter writer, STRING text) {
  299.       writer.writeNamespaces ();
  300.       writer.write (str: ">");
  301.       writer.setState (state: XmlWriter.contentWriterState);
  302.       writer.currentState.writeComment (writer: writer, text: text);
  303.    }
  304.  
  305.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  306.       writer.writeNamespaces ();
  307.       writer.write (str: ">");
  308.       writer.write (str: "\n");
  309.       writer.write (str: xmlstr);
  310.       writer.setState (state: XmlWriter.contentWriterState);
  311.    }
  312. }
  313.  
  314. /*****************************************************************************/
  315. /*
  316. ** This class is the XmlWriterState representing the state of an XmlWriter
  317. ** instance in which XML element content is being written.
  318. */
  319. /****************************************************************************/
  320. private CLASS XmlContentWriterState EXTENDS XmlWriterState {
  321.    // Public Methods
  322.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  323.       print "InvalidOperationException: Cannot write XML declaration inside element";
  324.    }
  325.  
  326.    METHOD writeDocumentEnd (XmlWriter writer) {
  327.       BOOLEAN done;
  328.       for (done = FALSE; done; ) {
  329.          if (writer.openElems.isEmpty ())
  330.             done = TRUE;
  331.          else
  332.             this.writeElementEnd (writer: writer);
  333.          }
  334.       writer.setState (state: XmlWriter.endWriterState);
  335.    }
  336.  
  337.    METHOD writeElementStart (XmlWriter writer, STRING prefix, 
  338.                              STRING localName, STRING ns) {
  339.       writer.newlineAndIndent ();
  340.       writer.writeElementStartInternal (prefix: prefix,
  341.          localName: localName, ns: ns);
  342.       writer.setState (state: XmlWriter.elementWriterState);
  343.    }
  344.  
  345.    METHOD writeElementEnd (XmlWriter writer) {
  346.       XmlOpenElement elem = writer.openElems.pop ();
  347.       if (elem.childCount)
  348.          writer.newlineAndIndent ();
  349.       writer.write (str: "</");
  350.       writer.write (str: elem.qName);
  351.       writer.write (str: ">");
  352.    }
  353.  
  354.    METHOD writeString (XmlWriter writer, STRING text) {
  355.       writer.write (str: writer.convertString (str: text));
  356.    }
  357.  
  358.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  359.                                STRING localName, STRING ns) {
  360.       print "InvalidOperationException: Cannot write attribute start outside element start";
  361.    }
  362.  
  363.    METHOD writeAttributeEnd (XmlWriter writer) {
  364.       print "InvalidOperationException: Cannot write attribute end outside element start";
  365.    }
  366.  
  367.    METHOD writeComment (XmlWriter writer, STRING text) {
  368.       writer.newlineAndIndent ();
  369.       writer.write (str: "<!-- ");
  370.       writer.write (str: text);
  371.       writer.write (str: " -->");
  372.    }
  373.  
  374.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  375.       writer.newlineAndIndent ();
  376.       writer.write (str: xmlstr);
  377.       writer.setState (state: XmlWriter.elementWriterState);
  378.    }
  379. }
  380.  
  381. /*****************************************************************************/
  382. /*
  383. ** This class is the XmlWriterState representing the state of an XmlWriter
  384. ** instance in which XML attribute value is being written.
  385. */
  386. /****************************************************************************/
  387. private CLASS XmlAttributeWriterState EXTENDS XmlWriterState {
  388.    // Public Methods
  389.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  390.       print "InvalidOperationException: Cannot write XML declaration inside attribute";
  391.    }
  392.  
  393.    METHOD writeDocumentEnd (XmlWriter writer) {
  394.       this.writeAttributeEnd (writer: writer);
  395.       this.writer.currentState.writeDocumentEnd (writer: writer);
  396.    }
  397.  
  398.    METHOD writeElementStart (XmlWriter writer, STRING prefix, 
  399.                              STRING localName, STRING ns) {
  400.       print "InvalidOperationException: Cannot write element start inside attribute";
  401.    }
  402.  
  403.    METHOD writeElementEnd (XmlWriter writer) {
  404.       print "InvalidOperationException: Cannot write element end inside attribute";
  405.    }
  406.  
  407.    METHOD writeString (XmlWriter writer, STRING text) {
  408.       text = writer.convertString (str: text);
  409.  
  410.       XmlOpenElement elem = writer.openElems.peek ();
  411.       if (!elem.openXmlLang && !elem.openXmlSpace)
  412.          writer.write (str: text);
  413.       else {
  414.          if (elem.openXmlLang)
  415.             elem.xmlLang = text;
  416.          else {
  417.             if (text == "default")
  418.               elem.xmlSpace = XmlSpace.Default;
  419.             else if (text == "preserve")
  420.               elem.xmlSpace = XmlSpace.Preserve;
  421.             else {
  422.                print "ArgumentException: Invalid xml:space value <" +
  423.                         text + ">";
  424.                return;
  425.                }
  426.             }
  427.          }
  428.    }
  429.  
  430.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  431.                                STRING localName, STRING ns) {
  432.       print "InvalidOperationException: Cannot write attribute start inside attribute";
  433.    }
  434.  
  435.    METHOD writeAttributeEnd (XmlWriter writer) {
  436.       XmlOpenElement elem = writer.openElems.peek ();
  437.  
  438.       if (elem.openXmlLang) {
  439.          writer.write (str: elem.xmlLang);
  440.          elem.openXmlLang = FALSE;
  441.          }
  442.  
  443.       if (elem.openXmlSpace) {
  444.          if (elem.xmlSpace == XmlSpace.Preserve)
  445.             writer.write (str: "preserve");
  446.          else if (elem.xmlSpace == XmlSpace.Default)
  447.             writer.write (str: "default");
  448.          elem.openXmlSpace = TRUE;
  449.          }
  450.  
  451.       writer.write (str: "\"");
  452.       writer.setState (state: XmlWriter.elementWriterState);
  453.    }
  454.  
  455.    METHOD writeComment (XmlWriter writer, STRING text) {
  456.       print "InvalidOperationException: Cannot write comment inside atribute";
  457.    }
  458.  
  459.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  460.       print "InvalidOperationException: Cannot write raw XML inside attribute";
  461.    }
  462. }
  463.  
  464. /*****************************************************************************/
  465. /*
  466. ** This class is the XmlWriterState representing the final state of an
  467. ** XmlWriter instance in which XML data has been written.
  468. */
  469. /****************************************************************************/
  470. private CLASS XmlEndWriterState EXTENDS XmlWriterState {
  471.    // Public Methods
  472.    METHOD writeDocumentStart (XmlWriter writer, STRING encoding) {
  473.       print "InvalidOperationException: Cannot write XML declaration after root element end";
  474.    }
  475.  
  476.    METHOD writeDocumentEnd (XmlWriter writer) {
  477.       print "InvalidOperationException: Cannot write document end after root element end";
  478.    }
  479.  
  480.    METHOD writeElementStart (XmlWriter writer, STRING prefix,
  481.                              STRING localName, STRING ns) {
  482.       print "InvalidOperationException: Cannot write element start after root element end";
  483.    }
  484.  
  485.    METHOD writeElementEnd (XmlWriter writer) {
  486.       print "InvalidOperationException: Cannot write element end after root element end";
  487.    }
  488.  
  489.    METHOD writeString (XmlWriter writer, STRING text) {
  490.       print "InvalidOperationException: Cannot write string content after root element end";
  491.    }
  492.  
  493.    METHOD writeAttributeStart (XmlWriter writer, STRING prefix,
  494.                                STRING localName, STRING ns) {
  495.       print "InvalidOperationException: Cannot write attribute start after root element end";
  496.    }
  497.  
  498.    METHOD writeAttributeEnd (XmlWriter writer) {
  499.       print "InvalidOperationException: Cannot write attribute end after root element end";
  500.    }
  501.  
  502.    METHOD writeComment (XmlWriter writer, STRING text) {
  503.       writer.newlineAndIndent ();
  504.       writer.write (str: "<!-- ");
  505.       writer.write (str: text);
  506.       writer.write (str: " -->");
  507.    }
  508.  
  509.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  510.       print "InvalidOperationException: Cannot write raw XML after root element end";
  511.    }
  512. }
  513.  
  514. /*****************************************************************************/
  515. /*
  516. ** This class represents an XML character entity.
  517. */
  518. /****************************************************************************/
  519. private CLASS XmlCharEntity {
  520.    // Fields
  521.    STRING name;
  522.    STRING character;
  523. }
  524.  
  525. /*****************************************************************************/
  526. /*
  527. ** This class represents an XML namespace.
  528. */
  529. /****************************************************************************/
  530. private CLASS XmlNamespace {
  531.    // Fields
  532.    STRING prefix;
  533.    STRING ns;
  534. }  
  535.  
  536. /*****************************************************************************/
  537. /*
  538. ** This class implements an open XML element.
  539. */
  540. /* @addNamespace Adds a namespace to the open XML element. */
  541. /* @generatePrefix Generates new prefix for undeclared namespace. */
  542. /* @lookupPrefix Returns the closest prefix defined in the current namespace scope for the given namespace. */
  543. /* @lookupNamespace Returns the namespace corresponding to given prefix */
  544. /****************************************************************************/
  545. private CLASS XmlOpenElement {
  546.    // Fields
  547.    STRING         qName;
  548.    STRING         defaultNs;
  549.    INTEGER        childCount;
  550.    LIST           namespaces;
  551.    XmlOpenElement parent;
  552.    STRING         xmlLang;
  553.    BOOLEAN        openXmlLang    = FALSE;
  554.    BOOLEAN        openXmlSpace   = FALSE;
  555.    INTEGER        xmlSpace       = XmlSpace.None;
  556.    INTEGER        genPrefixIndex = 0;
  557.  
  558.    // Public Methods
  559.    METHOD addNamespace (STRING prefix, STRING ns) {
  560.       if (!ns) {
  561.          print "ArgumentNullException: No namespace specified";
  562.          return;
  563.          }
  564.  
  565.       if ((prefix == "xml") && (ns != Xml.XmlNamespaceUri)) {
  566.          print "ArgumentException: xml prefix must be bound to URI <" +
  567.             Xml.XmlNamespaceUri + ">";
  568.          return;
  569.          }
  570.       else if (prefix == "xmlns") {
  571.          print "ArgumentException: prefix cannot be \"xmlns\"";
  572.          return;
  573.          }
  574.       else if (ns == Xml.XmlnsNamespaceUri) {
  575.          print "ArgumentException: URI <" + Xml.XmlnsNamespaceUri +
  576.             "> cannot be declared for any namespace";
  577.          return;
  578.          }
  579.  
  580.       if (prefix == "") {
  581.          this.defaultNs = ns;
  582.          }
  583.       else {
  584.          INTEGER i;
  585.          for (i = 0; i < this.namespaces.length (); i++) {
  586.             if (this.namespaces[i].ns == ns) {
  587.                break;
  588.                }
  589.             }
  590.  
  591.          if (i < this.namespaces.length ())
  592.             this.namespaces[i].prefix = prefix;
  593.          else {
  594.             XmlNamespace item = new (XmlNamespace, ns: ns, prefix: prefix);
  595.             this.namespaces.insert (obj: item);
  596.             }
  597.          }
  598.    }
  599.  
  600.    METHOD lookupPrefix (STRING ns) RETURNS (STRING prefix) {
  601.       if (!ns || ns == "") {
  602.          print "ArgumentNullException: Namespace cannot be empty";
  603.          prefix = "";
  604.          return;
  605.          }
  606.  
  607.       INTEGER i;
  608.       for (i = 0; i < this.namespaces.length (); i++) {
  609.          if (this.namespaces[i].ns == ns) {
  610.             break;
  611.             }
  612.          }
  613.  
  614.       if (i < this.namespaces.length ())
  615.          prefix = this.namespaces[i].prefix;
  616.    }
  617.  
  618.    METHOD lookupNamespace (STRING prefix) RETURNS (STRING ns) {
  619.       if (!prefix || prefix == "")
  620.          ns = this.defaultNs;
  621.       else {
  622.          INTEGER i;
  623.          for (i = 0; i < this.namespaces.length (); i++) {
  624.             if (this.namespaces[i].prefix == prefix) {
  625.                break;
  626.                }
  627.             }
  628.  
  629.          if (i < this.namespaces.length ())
  630.             ns = this.namespaces[i].ns;
  631.          }
  632.    }
  633.  
  634.    METHOD generatePrefix (STRING ns) RETURNS (STRING prefix) {
  635.       STRING  p;
  636.       BOOLEAN done = FALSE;
  637.       for (!done;) {
  638.          ++this.genPrefixIndex;
  639.  
  640.          p = "ns" + this.genPrefixIndex;
  641.  
  642.          if (!this.lookupNamespace (prefix: p)) {
  643.             this.addNamespace (prefix: p, ns: ns);
  644.             done = TRUE;
  645.             }
  646.          }
  647.    }
  648. }
  649.  
  650. /*****************************************************************************/
  651. /*
  652. ** This class implements a text buffer for holding unformatted XML data.
  653. */ 
  654. /* @create  creates an XmlBuffer instance for unformatted XML data or an XmlFormattedBuffer for formatted XML data. */
  655. /* @lookupPrefix Returns the closest prefix defined in the current namespace scope for the given namespace. */
  656. /* @append Appends given string to text buffer. */
  657. /* @clear Clears text buffer. */
  658. /* @canAppend Returns TRUE if given string can be appended to text buffer without exceeding buffer capacity; FALSE otherwise. */
  659. /* @toString Returns a copy of the text buffer. */
  660. /* @newlineAndIndent Appends newline and indentation to text buffer. */
  661. /****************************************************************************/
  662. private CLASS XmlBuffer {
  663.    // Constants
  664.    INTEGER CAPACITY            = 512;
  665.    INTEGER DEFAULT_INDENT_SIZE = 2;
  666.  
  667.    // Fields
  668.    STRING xmlStr = "";
  669.  
  670.    // Public Methods
  671.    METHOD create (BOOLEAN indent, INTEGER indentSize) RETURNS (XmlBuffer buf) {
  672.       if (indent) {
  673.          XmlFormattedBuffer fbuf = new (XmlFormattedBuffer);
  674.  
  675.          if (!indentSize)
  676.             indentSize = XmlBuffer.DEFAULT_INDENT_SIZE;
  677.  
  678.          INTEGER i;
  679.          for (i = 0; i < indentSize; i++)
  680.             fbuf.indentStr = fbuf.indentStr + " ";
  681.  
  682.          buf = fbuf;
  683.          }
  684.       else {
  685.          buf = new (XmlBuffer);
  686.          }
  687.    }
  688.  
  689.    METHOD append (STRING str) {
  690.       this.xmlStr = this.xmlStr + str;
  691.    }
  692.  
  693.    METHOD clear () {
  694.       this.xmlStr = "";
  695.    }
  696.  
  697.    METHOD canAppend (STRING str) RETURNS (BOOLEAN result) {
  698.       if (!str)
  699.          result = FALSE;
  700.       else
  701.          result = (this.xmlStr.length () + str.length () >= XmlBuffer.CAPACITY);
  702.    }
  703.  
  704.    METHOD toString () RETURNS (STRING str) {
  705.       str = this.xmlStr.Copy ();
  706.    }
  707.  
  708.    METHOD writeAttributeIndent (INTEGER level) {
  709.       this.xmlStr = this.xmlStr + " ";
  710.    }
  711.  
  712.    METHOD newlineAndIndent (INTEGER level) {
  713.    }
  714. }
  715.  
  716. /*****************************************************************************/
  717. /* 
  718. ** This class implements a text buffer for holding formatted XML data.
  719. */
  720. /****************************************************************************/
  721. private CLASS XmlFormattedBuffer EXTENDS XmlBuffer {
  722.    // Fields
  723.    STRING indentStr;
  724.  
  725.    // Public Methods
  726.    METHOD writeAttributeIndent (INTEGER level) {
  727.       this.newlineAndIndent (level: level);
  728.    }
  729.  
  730.    METHOD newlineAndIndent (INTEGER level) {
  731.       this.append (str: "\n");
  732.  
  733.       INTEGER i;
  734.       for (i = 0; i < level; i++)
  735.          this.append (str: this.indentStr);
  736.    }
  737. }
  738.  
  739. /*****************************************************************************/
  740. /*
  741. ** This class is the abstract base class of a writer for the generation of XML data.
  742. */
  743. /* @setState Sets the state of XMLWriter instance. */
  744. /* @writeDocumentStart Writes the XML declaration. */
  745. /* @writeDocumentEnd Writes end tags for any open XML elements or attributes and puts the writer in the End state. */
  746. /* @writeElementStart Writes specified XML element start tag. */
  747. /* @writeElementEnd Writes end tag for the last XML element started with 'writeElementStart' call. */
  748. /* @writeString Writes the given XML text content. */
  749. /* @writeAttributeStart Writes the start of an XML attribute. */
  750. /* @writeAttributeEnd Writes the end for last XML attribute started with 'writeAttributeStart' call. */
  751. /* @writeComment Writes an XML comment containing specified text. */
  752. /* @writeRaw Writes raw XML data from a string (with no validation). */
  753. /* @writeAttributeIndent Writes indentation for an XML attribute. */
  754. /* @newlineAndIndent Writes new line and indentation. */
  755. /* @convertString Replaces special character entities with escaped forms. */
  756. /* @flush Flushes the data stream. */
  757. /* @write Writes string to internal buffer. */
  758. /* @writeNamespaces Writes XML attributes for namespaces of an open Element. */
  759. /* @writeElementStartInternal Writes data for specified XML element start tag. */
  760. /****************************************************************************/
  761. CLASS XmlWriter {
  762.    // WriterState Constants
  763.    XmlAttributeWriterState attributeWriterState;
  764.    XmlContentWriterState   contentWriterState;
  765.    XmlElementWriterState   elementWriterState;
  766.    XmlPrologWriterState    prologWriterState;
  767.    XmlStartWriterState     startWriterState;
  768.    XmlEndWriterState       endWriterState;
  769.  
  770.    // Fields
  771.    XmlWriterState currentState;
  772.    XmlBuffer      xmlBuf;
  773.    STACK          openElems;
  774.  
  775.    // Public Methods
  776.    METHOD setState (XmlWriterState state) {
  777.       this.currentState = state;
  778.    }
  779.  
  780.    METHOD writeDocumentStart (STRING encoding) {
  781.       this.currentState.writeDocumentStart (writer: this, encoding: encoding);
  782.    }
  783.  
  784.    METHOD writeDocumentEnd () {
  785.       this.currentState.writeDocumentEnd (writer: this);
  786.    }
  787.  
  788.    METHOD writeElementStart (STRING prefix, STRING localName, STRING ns) {
  789.       this.currentState.writeElementStart (writer: this, prefix: prefix,
  790.          localName: localName, ns: ns);
  791.    }
  792.  
  793.    METHOD writeElementEnd () {
  794.       this.currentState.writeElementEnd (writer: this);
  795.    }
  796.  
  797.    METHOD writeElementString (STRING prefix, STRING localName,
  798.                               STRING ns, STRING text) {
  799.       this.writeElementStart (prefix: prefix, localName: localName, ns: ns);
  800.       this.writeString (text: text);
  801.       this.writeElementEnd ();
  802.    }
  803.  
  804.    METHOD writeString (STRING text) {
  805.       this.currentState.writeString (writer: this, text: text);
  806.    }
  807.  
  808.    METHOD writeAttributeStart (STRING prefix, STRING localName, STRING ns) {
  809.       this.currentState.writeAttributeStart (writer: this, prefix: prefix,
  810.          localName: localName, ns: ns);
  811.    }
  812.  
  813.    METHOD writeAttributeEnd () {
  814.       this.currentState.writeAttributeEnd (writer: this);
  815.    }
  816.  
  817.    METHOD writeAttributeString (STRING prefix, STRING localName,
  818.                                 STRING ns, STRING text) {
  819.       this.writeAttributeStart (prefix: prefix, localName: localName, ns: ns);
  820.       this.writeString (text: text);
  821.       this.writeAttributeEnd (); 
  822.    }
  823.  
  824.    METHOD writeComment (STRING text) {
  825.       this.currentState.writeComment (writer: this, text: text);
  826.    }
  827.  
  828.    METHOD writeRaw (XmlWriter writer, STRING xmlstr) {
  829.       this.currentState.writeRaw (writer: this, xmlstr: xmlstr);
  830.    }
  831.  
  832.    METHOD writeAttributeIndent () {
  833.       this.xmlBuf.writeAttributeIndent (level: this.openElems.getCount ());
  834.    }
  835.  
  836.    METHOD newlineAndIndent () {
  837.       this.xmlBuf.newlineAndIndent (level: this.openElems.getCount ());
  838.    }
  839.  
  840.    METHOD convertString (STRING str) RETURNS (STRING newStr) {
  841.       if (!str) {
  842.          newStr = "";
  843.          return ;
  844.          }
  845.  
  846.       LIST entities = (new (XmlCharEntity, name: "amp",  character: "&"),
  847.                        new (XmlCharEntity, name: "lt",   character: "<"),
  848.                        new (XmlCharEntity, name: "gt",   character: ">"),
  849.                        new (XmlCharEntity, name: "apos", character: "\'"),
  850.                        new (XmlCharEntity, name: "quot", character: "\""));
  851.  
  852.       LIST    substr;
  853.       INTEGER i;
  854.       newStr = str;
  855.       foreach (entities: entity) {
  856.          substr = newStr.split (delims: entity.character);
  857.  
  858.          for (i = 1; i < substr.length (); i++)
  859.             substr[0] = substr[0] + "&" + entity.name + ";" + substr[i];
  860.  
  861.          newStr = substr[0];
  862.          }
  863.    }
  864.  
  865.    METHOD flush () {
  866.    }
  867.  
  868.    // Protected Methods
  869.    METHOD write (STRING str) {
  870.    }
  871.  
  872.    METHOD writeNamespaces () {
  873.       if (!this.openElems.isEmpty ()) {
  874.          XmlOpenElement elem = this.openElems.peek ();
  875.  
  876.          STRING ns, prefix;
  877.          foreach (elem.namespaces: entry) {
  878.             ns     = entry.ns;
  879.             prefix = entry.prefix;
  880.             this.writeAttributeIndent ();
  881.             this.write (str: "xmlns:" + prefix + "=\"" + ns + "\"");
  882.             }
  883.  
  884.          if (elem.defaultNs != "") {
  885.             if (!elem.parent || (elem.defaultNs != elem.parent.defaultNs)) {
  886.                this.writeAttributeIndent ();
  887.                this.write (str: "xmlns=\"" + elem.defaultNs + "\"");
  888.                }
  889.             }
  890.          }
  891.    }
  892.  
  893.    METHOD writeElementStartInternal (STRING prefix,
  894.                                      STRING localName, STRING ns) {
  895.       STRING qName = localName;
  896.       if (prefix && prefix.length () != 0)
  897.          qName = prefix + ":" + qName; 
  898.  
  899.       XmlOpenElement parent;
  900.       if (!this.openElems.isEmpty ()) {
  901.          parent = this.openElems.peek ();
  902.          parent.childCount++;
  903.          }
  904.  
  905.       XmlOpenElement openElem = new (XmlOpenElement, qName: qName,
  906.          childCount: 0, parent: parent);
  907.       if (ns)
  908.          openElem.addNamespace (prefix: prefix, ns: ns);
  909.       this.openElems.push (item: openElem);
  910.  
  911.       this.write (str: "<");
  912.       this.write (str: qName);
  913.    }
  914. }
  915.  
  916. /*****************************************************************************/
  917. /*
  918. ** This class implements a writer for generating and writing XML data to a
  919. ** string.
  920. */
  921. /* @reset Clears text buffer. */
  922. /* @toString Returns text buffer contents. */
  923. /*****************************************************************************/
  924. CLASS XmlStringWriter EXTENDS XmlWriter {
  925.    // Fields
  926.    XmlBuffer xmlBuf;
  927.  
  928.    // Public Methods
  929.    METHOD write (STRING str) {
  930.       this.xmlBuf.append (str: str);
  931.    }
  932.  
  933.    METHOD reset () {
  934.       this.xmlBuf.clear ();
  935.    }
  936.  
  937.    METHOD toString () RETURNS (STRING str) {
  938.       str = this.xmlBuf.toString ();
  939.    }
  940. }
  941.  
  942. /*****************************************************************************/
  943. /*
  944. ** This class implements a writer for generating and writing XML data to a
  945. ** text file.
  946. */
  947. /*****************************************************************************/
  948. CLASS XmlFileWriter EXTENDS XmlWriter {
  949.    // Fields
  950.    STRING filename;
  951.  
  952.    // Public Methods
  953.    METHOD write (STRING str) {
  954.       if (!this.xmlBuf.canAppend (str: str))
  955.          this.flush ();
  956.       this.xmlBuf.append (str: str);
  957.    }
  958.  
  959.    METHOD flush () {
  960.       STRING xmlStr = this.xmlBuf.toString ();
  961.       xmlStr.Write (filename: this.filename, value: 1, mode: "a");
  962.       this.xmlBuf.clear ();
  963.    }
  964. }
  965.  
  966. /*****************************************************************************/
  967. /*
  968. ** This class implements a factory for creating an XmlWriter instance.
  969. */
  970. /* @createStringWriter Creates an XmlStringWriter instance. */
  971. /* @createFileWriter Creates an XmlFileWriter instance. */
  972. /****************************************************************************/
  973. CLASS XmlWriterFactory {
  974.    // Public Methods
  975.    METHOD createStringWriter (BOOLEAN indent, INTEGER indentSize)
  976.      RETURNS (XmlStringWriter writer) {
  977.       writer              = new (XmlStringWriter);
  978.       writer.xmlBuf       = XmlBuffer.create (indent: indent,
  979.                                indentSize: indentSize);
  980.       writer.currentState = XmlWriter.startWriterState;
  981.    }
  982.  
  983.    METHOD createFileWriter (STRING filename, BOOLEAN indent, INTEGER indentSize)
  984.      RETURNS (XmlFileWriter writer) {
  985.       writer              = new (XmlFileWriter, filename: filename);
  986.       writer.xmlBuf       = XmlBuffer.create (indent: indent,
  987.                                indentSize: indentSize);
  988.       writer.currentState = XmlWriter.startWriterState;
  989.    }
  990. }
  991.